package com.bjh.proxy;

import com.bjh.annotation.MyResource;
import com.bjh.annotation.MyScan;
import com.bjh.annotation.Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

@Slf4j
@Component
public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware,ApplicationContextAware{

    private String packageValue;


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        String[] candidateNames = registry.getBeanDefinitionNames();
        for(String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            String beanClassName = beanDef.getBeanClassName();
            try{
                Class<?> aClass = Class.forName(beanClassName);
                if(aClass.isAnnotationPresent(MyScan.class)) {
                    String basePackage = aClass.getAnnotation(MyScan.class).value();
                    Set<Class<?>> beanClazzs = scannerPackages(basePackage);

                    for(Class clazz : beanClazzs) {

                        registerInterface(clazz,registry);

                        if(clazz.isAnnotationPresent(Service.class)) {
                            Class[] interfaces = clazz.getInterfaces();
                            Class interClazz = interfaces[0];
                            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(interClazz);
                            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
                            definition.getConstructorArgumentValues().addGenericArgumentValue(interClazz);

                            //注意，这里的BeanClass是生成Bean实例的工厂，不是Bean本身。
                            // FactoryBean是一种特殊的Bean，其返回的对象不是指定类的一个实例，
                            // 其返回的是该工厂Bean的getObject方法所返回的对象。
                            definition.setBeanClass(ServiceFactory.class);


                            //这里采用的是byType方式注入，类似的还有byName等
                            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
                            log.info("BeanName = {}",interfaces[0].getSimpleName());
                            registry.registerBeanDefinition(interClazz.getSimpleName(), definition);
                        }
                    }


                }
            }catch (Exception e) {

            }

        }

    }

    private void registerInterface(Class clazz,BeanDefinitionRegistry registry) {
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field field : declaredFields) {
            MyResource annotation = field.getAnnotation(MyResource.class);
            if(annotation != null) {
                Class<?> interClazz = field.getType();
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(interClazz);
                GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
                definition.getConstructorArgumentValues().addGenericArgumentValue(interClazz);

                //注意，这里的BeanClass是生成Bean实例的工厂，不是Bean本身。
                // FactoryBean是一种特殊的Bean，其返回的对象不是指定类的一个实例，
                // 其返回的是该工厂Bean的getObject方法所返回的对象。
                definition.setBeanClass(ServiceFactory.class);

                //这里采用的是byType方式注入，类似的还有byName等
                definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
                registry.registerBeanDefinition(interClazz.getSimpleName(), definition);
            }
        }
    }

    private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    private MetadataReaderFactory metadataReaderFactory;

    /**
     * 根据包路径获取包及子包下的所有类
     * @param basePackage basePackage
     * @return Set<Class<?>> Set<Class<?>>
     */
    private Set<Class<?>> scannerPackages(String basePackage) {
        Set<Class<?>> set = new LinkedHashSet<>();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + DEFAULT_RESOURCE_PATTERN;
        try {
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    String className = metadataReader.getClassMetadata().getClassName();
                    Class<?> clazz;
                    try {
                        clazz = Class.forName(className);
                        set.add(clazz);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return set;
    }

    protected String resolveBasePackage(String basePackage) {
        return ClassUtils.convertClassNameToResourcePath(this.getEnvironment().resolveRequiredPlaceholders(basePackage));
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    private ResourcePatternResolver resourcePatternResolver;

    private ApplicationContext applicationContext;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private Environment getEnvironment() {
        return applicationContext.getEnvironment();
    }

    //@Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MyScan.class.getName()));
        if (mapperScanAttrs != null) {
            this.registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry);
        }
    }

    void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {
//        List<String> basePackages = new ArrayList();
//        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
//        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
//        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
        String[] basePackages = annoAttrs.getStringArray("value");
        //拿到第一个元素
        String packageValue = basePackages[0];
        System.out.println("需要扫描的路径是：" + packageValue);
        this.packageValue = packageValue;

    }
}
